package fractal.fractal;

import fractal.ColorMap;
import fractal.FractalColor;
import fractal.FractalRecorder;
import fractal.filter.FractalFilter;
import jinyilw.common.SettingTools;
import jinyilw.common.ThreadTools;
import jinyilw.common.log.LogTools;
import jinyilw.common.math.MPoint;
import jinyilw.swing.image.ImageTools;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class Fractal implements Cloneable
{
	protected int id, width, height, maxN = 300; // 分形参数
	protected double xmin, ymin, dxy;
	protected double R;
	protected double angle = 0, rotateX, rotateY;
	protected double sff = 1;// X轴旋转系数

	protected double rotateCos = 1, rotateSin = 0;
	protected boolean isCenter = false;
	protected boolean isXRotate = false;
	protected boolean isMove = false;
	protected double[] xMins, xDxys;

	protected static final int zeroPix = 0;
	protected int[] colorSpace = FractalColor.getNormalColorSpace();
	protected FractalFilter filter = FractalFilter.filterArr[1];
	protected ColorMap colorMap;
	protected int[] pixels;
	protected int[] nDatas;

	protected int backColorPix = 0;
	protected BufferedImage image;

	protected static final int maxCore = ThreadTools.maxCore;

	static
	{
		LogTools.info("CPU Core Num:" + maxCore);
	}

	private FractalRecorder recorder;// 记录数据
	private boolean isRecorderInit = false;

	public Fractal()
	{
	}

	public void initialise()
	{
	}

	public int calculateN(double x, double y, int maxN, double R)
	{
		return 0;
	}

	@Override
	public Fractal clone()
	{
		try
		{
			Fractal fractal = (Fractal) super.clone();
			fractal.colorMap = colorMap.clone();
			return fractal;
		} catch (CloneNotSupportedException e)
		{
			e.printStackTrace();
			return null;
		}
	}

	private Thread[] createThreads(int core, int w, int h)
	{
		Thread[] threads = new Thread[core];
		int dx = w / core, l = core - 1;
		int steps = 0;
		for (int i = 0; i < l; i++)
		{
			threads[i] = new MThread(0, h, steps, steps + dx);
			steps += dx;
		}
		threads[l] = new MThread(0, h, steps, w);
		return threads;
	}

	private class MThread extends Thread
	{
		int r1, r2, c1, c2;

		public MThread(int r1, int r2, int c1, int c2)
		{
			this.r1 = r1;
			this.r2 = r2;
			this.c1 = c1;
			this.c2 = c2;
		}

		@Override
		public void run()
		{
			calculatePartFractal(r1, r2, c1, c2);
		}
	}

	public BufferedImage getFractalImage(int core)
	{
		return getFractalImage(null, core);
	}

	public BufferedImage getSaveFractalImage(int core)
	{
		return getSaveFractalImage(null, core);
	}

	public synchronized BufferedImage getFractalImage(BufferedImage img,
			int core)
	{
		if (!createImage(img))
			return null;
		DataBuffer dataBuffer = image.getRaster().getDataBuffer();
		if (filter != null)
		{
			if (id == 5)
				filter.filter(pixels, width, height, dataBuffer, backColorPix, null,
						nDatas, core);
			else
				filter.filter(pixels, width, height, dataBuffer, backColorPix, colorMap,
						nDatas, core);
		} else
		{
			int l = width * height;
			for (int i = 0; i < l; i++)
				if (pixels[i] == zeroPix)
					dataBuffer.setElem(i, backColorPix);
				else
					dataBuffer.setElem(i, pixels[i]);
		}
		return image;
	}

	//	public synchronized BufferedImage getNewFractalImage(BufferedImage img)
	//	{
	//		if (!createImage(img))
	//			return null;
	//		DataBuffer dataBuffer = image.getRaster().getDataBuffer();
	//		int l = W * H;
	//		filterPixels = new int[l];
	//		System.arraycopy(pixels, 0, filterPixels, 0, l);
	//		if (imageOp != null)
	//		{
	//			imageOp.filter(filterPixels, filterPixels, W, H);
	//		}
	//
	//		for (int i = 0; i < l; i++)
	//			if (pixels[i] == zeroPix)
	//				dataBuffer.setElem(i, backColorPix);
	//			else
	//				dataBuffer.setElem(i, filterPixels[i]);
	//		return image;
	//	}

	// SurfaceBlur surfaceBlur = new SurfaceBlur();

	public synchronized BufferedImage getSaveFractalImage(BufferedImage img,
			int core)
	{
		if (!createImage(img))
			return null;
		DataBuffer dataBuffer = image.getRaster().getDataBuffer();
		if (filter != null)
		{
			ColorMap map = id == 5 ? null : colorMap;
			int[] outPixels = new int[width * height];
			filter.filter(pixels, outPixels, width, height, backColorPix, map, nDatas,
					core);
			for (int i = 0; i < outPixels.length; i++)
				dataBuffer.setElem(i, outPixels[i]);
			//			surfaceBlur.filter(outPixels, W, H, dataBuffer, backColorPix, null,
			//					null, core);
		} else
		{
			int l = width * height;
			for (int i = 0; i < l; i++)
				if (pixels[i] == zeroPix)
					dataBuffer.setElem(i, backColorPix);
				else
					dataBuffer.setElem(i, pixels[i]);
		}
		return image;
	}

	private boolean createImage(BufferedImage img)
	{
		if (pixels == null || nDatas == null || colorSpace == null)
			return false;
		if (img != null)
		{
			if (img.getWidth() == width && img.getHeight() == height)
				image = img;
			else if (image == null || img.getType() != image.getType() || image.getWidth() != width
					|| image.getHeight() != height)
			{
				image = new BufferedImage(width, height, img.getType());
			}
		} else
		{
			if (image == null)
				image = ImageTools.createOpaqueImage(width, height);
			else if (image.getWidth() != width || image.getHeight() != height)
				image = new BufferedImage(width, height, image.getType());
		}
		return true;
	}

	public void setFractalData(FractalData fd)
	{
		if (fd == null)
			return;
		this.maxN = fd.maxN;
		this.xmin = fd.xmin;
		this.ymin = fd.ymin;
		this.dxy = fd.dxy;
		this.R = fd.R;
		this.rotateX = fd.rotateX;
		this.rotateY = fd.rotateY;
		if (this.angle != fd.angle)
		{
			this.angle = fd.angle;
			rotateCos = Math.cos(angle);
			rotateSin = Math.sin(angle);
		}
		sff = fd.sff;
		isXRotate = sff != 1;
		if (width != fd.W || height != fd.H)
		{
			int ww = width, hh = height;
			width = fd.W;
			height = fd.H;
			expandSize(ww, hh);
		}
	}

	public FractalData getNewRecordData()
	{
		center();
		return new FractalData(width, height, maxN, xmin, ymin, dxy, R, angle, rotateX,
				rotateY, sff).toRecordData();
	}

	public FractalData getCurrentRecordData()
	{
		if (isRecorderInit)
			return recorder.getCurrentData();
		return null;
	}

	public boolean isRecordInit()
	{
		return isRecorderInit;
	}

	public void initRecordData()
	{
		if (recorder == null)
			recorder = new FractalRecorder();
		recorder.initRecordData(this);
		isRecorderInit = true;
	}

	public FractalRecorder getRecorder()
	{
		return recorder;
	}

	public void setFractalPNData(int[] pixels, int[] nDatas)
	{
		this.pixels = pixels;
		this.nDatas = nDatas;
	}

	public Fractal expandSize(int newW, int newH)
	{
		if ((width == newW && height == newH) || newW == 0 || newH == 0)
			return this;
		if (width == 0 || height == 0)
		{
			width = newW;
			height = newH;
			return this;
		}
		double dd, dh, ww = newW, hh = newH;
		if (ww / width >= hh / height)
		{
			dd = (width * dxy) / ww;
			dh = hh * width / ww;
			ymin = ymin + (height - dh) / 2 * dxy;
			dxy = dd;
		} else
		{
			dd = (height * dxy) / hh;
			dh = ww * height / hh;
			xmin = xmin + (width - dh) / 2 * dxy;
			dxy = dd;
		}
		width = newW;
		height = newH;
		return this;
	}

	public void setSize(int w, int h)
	{
		width = w;
		height = h;
	}

	public void setId(int id)
	{
		this.id = id;
	}

	public void setMaxN(int maxN)
	{
		this.maxN = maxN;
		if (isRecorderInit)
			recorder.getCurrentData().maxN = maxN;
	}

	public void grow()
	{
		maxN++;
	}

	public void melt()
	{
		maxN--;
	}

	public void setR(double r)
	{
		R = r;
		if (isRecorderInit)
			recorder.getCurrentData().R = R;
	}

	public void setAngle(double angle)
	{
		if (this.angle != angle)
		{
			this.angle = angle;
			rotateCos = Math.cos(angle);
			rotateSin = Math.sin(angle);
			if (isRecorderInit)
				recorder.getCurrentData().angle = angle;
		}
	}

	public void setSff(double sff)
	{
		if (sff > 0 && this.sff != sff)
		{
			this.sff = sff;
			isXRotate = sff != 1;
			if (isRecorderInit)
				recorder.getCurrentData().sff = sff;
		}
	}

	void updateSff()
	{
		if (!isXRotate)
			return;
		if (xMins == null || xMins.length != height)
		{
			xMins = new double[height];
			xDxys = new double[height];
		}
		xMins[0] = xmin;
		xDxys[0] = dxy;
		for (int i = 1; i < height; i++)
		{
			xDxys[i] = xDxys[i - 1] * sff;
			xMins[i] = xmin + width * (dxy - xDxys[i]) * 0.5;
		}
	}

	public int getWidth()
	{
		return width;
	}
	public int getHeight()
	{
		return height;
	}
	public int getMaxN()
	{
		return maxN;
	}
	public double getR()
	{
		return R;
	}
	public double getSff()
	{
		return sff;
	}

	public double getXMin()
	{
		return xmin;
	}
	public double getYMin()
	{
		return ymin;
	}
	public double getDxy()
	{
		return dxy;
	}

	public FractalFilter getFilter()
	{
		return filter;
	}

	public void setColorSpace(int[] colorSpace)
	{
		this.colorSpace = colorSpace;
	}
	public void setFilter(FractalFilter filter)
	{
		this.filter = filter;
	}

	public void setFilter(int filterID)
	{
		filter = FractalFilter.filterArr[filterID];
	}

	public void setColorMap(ColorMap colorMap)
	{
		this.colorMap = colorMap;
	}

	public void setBackColorPix(int pix)
	{
		if (pix == backColorPix)
			return;
		backColorPix = pix;
		//		int mxy = W * H;
		//		for (int i = 0; i < mxy; i++)
		//			if (nDatas[i] == maxN)
		//				pixels[i] = pix;
	}

	public int getBackColorPix()
	{
		return backColorPix;
	}

	public void setBackColor(Color backColor)
	{
		if (backColor != null)
			setBackColorPix(backColor.getRGB());
	}

	public void calculatePartFractal(int r1, int r2, int c1, int c2)
	{
		if (isXRotate)
		{
			calculatePartX(r1, r2, c1, c2);
			return;
		}
		int n, rm, l = colorSpace.length;
		double x, y, xi, yi;
		double xx, yy, yysi, yyco;
		double xc = xmin + c1 * dxy;
		yi = ymin + r1 * dxy;
		for (int row = r1; row < r2; row++)
		{
			yy = yi - rotateY;
			yysi = yy * rotateSin;
			yyco = yy * rotateCos;
			rm = row * width;
			xi = xc;
			for (int con = c1; con < c2; con++)
			{
				xx = xi - rotateX;
				x = rotateX + xx * rotateCos - yysi;
				y = rotateY + xx * rotateSin + yyco;
				n = calculateN(x, y, maxN, R);
				nDatas[rm + con] = n;
				if (n < maxN)
					pixels[rm + con] = colorSpace[n % l];
				else
					pixels[rm + con] = zeroPix;
				xi += dxy;
			}
			yi += dxy;
		}
	}

	void calculatePartX(int r1, int r2, int c1, int c2)
	{
		int n, rm, l = colorSpace.length;
		double x, y, xi, yi, ddxy;
		double xx, yy, yysi, yyco;
		yi = ymin + r1 * dxy;
		for (int row = r1; row < r2; row++)
		{
			yy = yi - rotateY;
			yysi = yy * rotateSin;
			yyco = yy * rotateCos;
			rm = row * width;
			ddxy = xDxys[row];
			xi = xMins[row] + ddxy * c1;
			for (int con = c1; con < c2; con++)
			{
				xx = xi - rotateX;
				x = rotateX + xx * rotateCos - yysi;
				y = rotateY + xx * rotateSin + yyco;
				n = calculateN(x, y, maxN, R);
				nDatas[rm + con] = n;
				if (n < maxN)
					pixels[rm + con] = colorSpace[n % l];
				else
					pixels[rm + con] = zeroPix;
				xi += ddxy;
			}
			yi += dxy;
		}
	}

	public int[] createFractalPixels()
	{
		if (pixels == null || pixels.length != width * height)
		{
			pixels = new int[width * height];
			nDatas = new int[width * height];
		}
		return calculateFractalInThread(maxCore / 2);
	}

	public int[] calculateFractalInThread(int core)
	{
		if (core > maxCore)
			core = maxCore;
		if (!isMove)
			updateSff();
		if (core > 1)
			ThreadTools.startAndJoin(createThreads(core, width, height));
		else
			calculatePartFractal(0, height, 0, width);

		return pixels;
	}

	public void chooseNewAreaFractal(int x0, int y0, int x1, int y1)
	{
		if (isXRotate)
		{
			xmin = xMins[y0] + xDxys[y0] * x0;
			ymin = ymin + dxy * y0;
			dxy = (x1 - x0) * xDxys[y0] / width;
		} else
		{
			xmin = xmin + dxy * x0;
			ymin = ymin + dxy * y0;
			dxy = (x1 - x0) * dxy / width;
		}
		isCenter = false;
		if (isRecorderInit)
			recorder.recordNewData(this);
	}

	public void moveDown(int l)
	{
		if (l > 0)
		{
			isMove = true;
			if (isXRotate)
			{
				ymin = ymin - dxy * l * 2;
				calculateFractalInThread(maxCore);
			} else
			{
				int C = width * l;
				for (int i = width * height - 1; i > C; i--)
				{
					pixels[i] = pixels[i - width * l];
					nDatas[i] = nDatas[i - width * l];
				}
				ymin = ymin - dxy * l;
				calculatePartFractal(0, l, 0, width);
			}
			isMove = false;
			isCenter = false;
			if (isRecorderInit)
				recorder.recordCurrentData(this);
		}
	}

	public void moveUp(int l)
	{
		if (l > 0)
		{
			isMove = true;
			if (isXRotate)
			{
				ymin = ymin + dxy * l * 2;
				calculateFractalInThread(maxCore);
			} else
			{
				int C = width * (height - l);
				for (int i = 0; i < C; i++)
				{
					pixels[i] = pixels[width * l + i];
					nDatas[i] = nDatas[width * l + i];
				}
				ymin = ymin + dxy * l;
				calculatePartFractal(height - l, height, 0, width);
			}
			isMove = false;
			isCenter = false;
			if (isRecorderInit)
				recorder.recordCurrentData(this);
		}
	}

	public void moveLeft(int l)
	{
		if (l > 0)
		{
			isMove = true;
			if (isXRotate)
			{
				double ll = dxy * l * 2;
				xmin += ll;
				for (int i = 0; i < height; i++)
					xMins[i] += ll;
				calculateFractalInThread(maxCore);
			} else
			{
				int C = width - l;
				for (int row = 0; row < height; row++)
					for (int con = 0; con < C; con++)
					{
						pixels[row * width + con] = pixels[row * width + con + l];
						nDatas[row * width + con] = nDatas[row * width + con + l];
					}
				xmin = xmin + dxy * l;
				calculatePartFractal(0, height, width - l, width);
			}
			isMove = false;
			isCenter = false;
			if (isRecorderInit)
				recorder.recordCurrentData(this);
		}
	}

	public void moveRight(int l)
	{
		if (l > 0)
		{
			isMove = true;
			if (isXRotate)
			{
				double ll = dxy * l * 2;
				xmin -= ll;
				for (int i = 0; i < height; i++)
					xMins[i] -= ll;
				calculateFractalInThread(maxCore);
			} else
			{
				for (int row = 0; row < height; row++)
					for (int con = width - 1; con >= l; con--)
					{
						pixels[row * width + con] = pixels[row * width + con - l];
						nDatas[row * width + con] = nDatas[row * width + con - l];
					}
				xmin = xmin - (dxy * l);
				calculatePartFractal(0, height, 0, l);
			}
			isMove = false;
			isCenter = false;
			if (isRecorderInit)
				recorder.recordCurrentData(this);
		}
	}

	public void largerStep(double sfFacter)
	{
		xmin = xmin + 0.5 * width * dxy * sfFacter / 100;
		ymin = ymin + 0.5 * height * dxy * sfFacter / 100;
		dxy = (1 - sfFacter / 100) * dxy;
		if (isRecorderInit)
			recorder.recordCurrentData(this);
	}

	public void smallerStep(double sfFacter)
	{
		xmin = xmin - 0.5 * width * dxy * sfFacter / 100;
		ymin = ymin - 0.5 * height * dxy * sfFacter / 100;
		dxy = (1 + sfFacter / 100) * dxy;
		if (isRecorderInit)
			recorder.recordCurrentData(this);
	}

	public void mouseLargerStep(double scaleX, double scaleY, double sfFacter)
	{
		xmin = xmin + scaleX * width * dxy * sfFacter / 100;
		ymin = ymin + scaleY * height * dxy * sfFacter / 100;
		dxy = (1 - sfFacter / 100) * dxy;
		isCenter = false;
		if (isRecorderInit)
			recorder.recordCurrentData(this);
	}

	public void mouseSmallerStep(double scaleX, double scaleY, double sfFacter)
	{
		xmin = xmin - scaleX * width * dxy * sfFacter / 100;
		ymin = ymin - scaleY * height * dxy * sfFacter / 100;
		dxy = (1 + sfFacter / 100) * dxy;
		isCenter = false;
		if (isRecorderInit)
			recorder.recordCurrentData(this);
	}

	public boolean moveForward()
	{
		if (isRecorderInit)
		{
			FractalData data = recorder.getForwardData();
			if (data != null)
			{
				setFractalData(data);
				return true;
			}
		}
		return false;
	}

	public boolean moveBackward()
	{
		if (isRecorderInit)
		{
			FractalData data = recorder.getBackwardData();
			if (data != null)
			{
				setFractalData(data);
				return true;
			}
		}
		return false;
	}

	public void rotateFractal(double ang)
	{
		if (ang == 0)
			return;
		center();
		angle = angle + ang;
		rotateCos = Math.cos(angle);
		rotateSin = Math.sin(angle);
		if (isRecorderInit)
			recorder.recordCurrentData(this);
	}

	public void center()
	{
		if (isCenter)
			return;
		MPoint minPoint = new MPoint(xmin, ymin);
		MPoint centerPoint = new MPoint(xmin + dxy * width / 2, ymin + dxy * height / 2);
		minPoint.rotate(angle, rotateX, rotateY);
		centerPoint.rotate(angle, rotateX, rotateY);
		rotateX = centerPoint.x;
		rotateY = centerPoint.y;
		minPoint.rotate(-angle, rotateX, rotateY);
		xmin = minPoint.x;
		ymin = minPoint.y;
		isCenter = true;
	}

	public void xRotate(int f)
	{
		double newSff = sff - f * 0.0001;
		setSff(newSff);
	}

	public void calculateNewNFractal(int newN)
	{
		if (newN < 0 || maxN == newN)
			return;
		int preN = maxN;
		maxN = newN;
		int mxy = width * height;
		int row, con, n, l = colorSpace.length;
		MPoint point = new MPoint();
		if (preN < maxN)
		{
			for (int i = 0; i < mxy; i++)
			{
				if (nDatas[i] == preN)
				{
					row = i / width;
					con = i % width;
					if (isXRotate)
						point.setPoint(xMins[row] + con * xDxys[row],
								ymin + row * dxy);
					else
						point.setPoint(xmin + con * dxy, ymin + row * dxy);
					point.rotate(angle, rotateX, rotateY);
					n = calculateN(point.x, point.y, maxN, R);
					nDatas[i] = n;
					if (n < maxN)
						pixels[i] = colorSpace[n % l];
					else
						pixels[i] = zeroPix;
				}
			}
		} else
		{
			for (int i = 0; i < mxy; i++)
			{
				if (nDatas[i] >= maxN)
				{
					pixels[i] = zeroPix;
					nDatas[i] = maxN;
				}
			}
		}
		if (isRecorderInit)
			recorder.getCurrentData().maxN = maxN;
	}

	public void calculateNewColorFractal()
	{
		int mxy = width * height;
		for (int i = 0; i < mxy; i++)
			if (nDatas[i] != maxN)
				pixels[i] = colorSpace[nDatas[i] % colorSpace.length];
	}

	public void expandRightDownSide(int eW, int eH)
	{
		if (eW < 5 || eH < 5)
			return;
		// expandSize(eW, eH);
		xmin = xmin - ((double) eW - width) * dxy / 2;
		ymin = ymin - ((double) eH - height) * dxy / 2;
		width = eW;
		height = eH;
		pixels = new int[width * height];
		nDatas = new int[width * height];
		calculateFractalInThread(maxCore);
		if (isRecorderInit)
			recorder.recordCurrentData(this);
		//		if (isXrotate)
		//		{
		//			expandSize(eW, eH);
		//			pixels = new int[W * H];
		//			nDatas = new int[W * H];
		//			calculateFractalInThread(maxCore);
		//			return;
		//		}
		//		int[] p = pixels, rN = nDatas;
		//		int preW = W, preH = H;
		//		W = eW;
		//		H = eH;
		//		pixels = new int[W * H];
		//		nDatas = new int[W * H];
		//
		//		for (int row = 0; row < H && row < preH; row++)
		//			for (int con = 0; con < W && con < preW; con++)
		//			{
		//				pixels[row * W + con] = p[row * preW + con];
		//				nDatas[row * W + con] = rN[row * preW + con];
		//			}
		//		if (W > preW && H > preH)
		//		{
		//			calculatePartFractal(preH, H, 0, W);
		//			calculatePartFractal(0, preH, preW, W);
		//		} else if (W > preW && H <= preH)
		//			calculatePartFractal(0, H, preW, W);
		//		else if (W <= preW && H > preH)
		//			calculatePartFractal(preH, H, 0, W);
	}

	public void writeToDataStream(DataOutputStream dos) throws IOException
	{
		dos.writeInt(width);
		dos.writeInt(height);
		dos.writeInt(maxN);
		dos.writeDouble(xmin);
		dos.writeDouble(ymin);
		dos.writeDouble(dxy);
		dos.writeDouble(R);
		dos.writeDouble(angle);
		dos.writeDouble(rotateX);
		dos.writeDouble(rotateY);
		dos.writeDouble(sff);
	}

	public void readFromDataStream(DataInputStream dis, int version)
			throws IOException
	{
		if (version >= 6)
		{
			newReadFromDataStream(dis);
			return;
		}
		width = dis.readInt();
		height = dis.readInt();
		maxN = dis.readInt();
		xmin = dis.readDouble();
		ymin = dis.readDouble();
		dxy = dis.readDouble();
		R = dis.readDouble();
		double ang = dis.readDouble();
		if (this.angle != ang)
		{
			this.angle = ang;
			rotateCos = Math.cos(angle);
			rotateSin = Math.sin(angle);
		}
		rotateX = dis.readDouble();
		rotateY = dis.readDouble();
		sff = 1;
		isXRotate = false;
	}

	public void newReadFromDataStream(DataInputStream dis) throws IOException
	{
		width = dis.readInt();
		height = dis.readInt();
		maxN = dis.readInt();
		xmin = dis.readDouble();
		ymin = dis.readDouble();
		dxy = dis.readDouble();
		R = dis.readDouble();
		double ang = dis.readDouble();
		if (this.angle != ang)
		{
			this.angle = ang;
			rotateCos = Math.cos(angle);
			rotateSin = Math.sin(angle);
		}
		rotateX = dis.readDouble();
		rotateY = dis.readDouble();
		sff = dis.readDouble();
		isXRotate = sff != 1;
	}

	public void putCustomData()
	{
		SettingTools.putDefaultSettingData("fractalID", id);
		SettingTools.putDefaultSettingData("maxN", maxN);
		SettingTools.putDefaultSettingData("xmin", xmin);
		SettingTools.putDefaultSettingData("ymin", ymin);
		SettingTools.putDefaultSettingData("dxy", dxy);
		SettingTools.putDefaultSettingData("R", R);
		SettingTools.putDefaultSettingData("angle", angle);
		SettingTools.putDefaultSettingData("rotateX", rotateX);
		SettingTools.putDefaultSettingData("rotateY", rotateY);
		SettingTools.putDefaultSettingData("sff", sff);
	}

	public void getCustomData()
	{
		maxN = SettingTools.getDefaultInt("maxN", maxN);
		xmin = SettingTools.getDefaultDouble("xmin", xmin);
		ymin = SettingTools.getDefaultDouble("ymin", ymin);
		dxy = SettingTools.getDefaultDouble("dxy", dxy);
		R = SettingTools.getDefaultDouble("R", R);
		rotateX = SettingTools.getDefaultDouble("rotateX", rotateX);
		rotateY = SettingTools.getDefaultDouble("rotateY", rotateY);
		double ang = SettingTools.getDefaultDouble("angle", angle);
		if (this.angle != ang)
		{
			this.angle = ang;
			rotateCos = Math.cos(angle);
			rotateSin = Math.sin(angle);
		}
		// System.out.println(rotateCos + ":" + rotateSin);
		sff = SettingTools.getDefaultDouble("sff", sff);
		isXRotate = sff != 1;
	}
}
